package property

import (
	"container/list"
	//"fmt"
	"iot-base/common/cache"
	"iot-base/common/logger"
	"iot-base/common/notify"
	"iot-base/common/propertytype"
	"time"
)

//数据存储到一个cache中可以重新应用

var listContext = list.New()

//ValueContext 上下文
type ValueContext struct {
	ValuePtr    *propertytype.Value
	Context     map[string]interface{}
	ThingPtr    *cache.ThingInfo
	PropertyPtr *cache.ItemInfo
	FValue      float64
	IValue      int64
	SValue      string
	//DisplayValue      string
	Type              string
	PickTime          time.Time
	FLastValue        float64
	LastPickTime      int64
	ParentPropertyPtr *cache.ItemInfo
	Display           string
	//IsFree   bool
}

func getContext(pValue *propertytype.Value) *ValueContext {

	if listContext.Len() == 0 {
		item := &ValueContext{
			ValuePtr: pValue,
			Context:  make(map[string]interface{}),

			//IsFree:   false,
		}
		return item
	}
	back := listContext.Back()
	if back != nil && back.Value != nil {
		pc, _ := back.Value.(*ValueContext)
		listContext.Remove(back)
		if pc != nil {
			pc.ValuePtr = pValue
			pc.Context = make(map[string]interface{})
			pc.ThingPtr = nil
			pc.PropertyPtr = nil
			pc.ParentPropertyPtr = nil
			return pc
		}
	}
	return nil
}

func freeContext(context *ValueContext) {
	listContext.PushFront(context)
}

var contextlistFreeChan = make(chan *ValueContext)

//var contextlistChan = make(chan *ValueContext)
var valuelistChan = make(chan *propertytype.Value)

//DataHandler 数据检查 数据存储 数据产生新的数据或告警
func DataHandler(pValue *propertytype.Value) {
	//fmt.Println("datahander------------------begin-----------", pValue.TID, pValue.PID, pValue.SubID, pValue.Mindex)
	valuelistChan <- pValue
	//fmt.Println("datahander---------------finished--------------", pValue.TID, pValue.PID, pValue.SubID, pValue.Mindex)
}

//初始化数据处理进程
func init() {

}

func InitDataHandler() {
	initDB()
	go contextManagerThrd()

	notify.SubRawData(func(pValue *propertytype.Value) {
		DataHandler(pValue)
	})
}

func contextManagerThrd() {
	cacheTidTime := make(map[string]int64)
	for {
		select {
		case pContext := <-contextlistFreeChan:
			freeContext(pContext)
		case pValue := <-valuelistChan:
			now := time.Now().Unix()
			activeTime := cacheTidTime[pValue.TID]
			if now-activeTime > 100 {
				cache.SetLastAliveTime(pValue.TID, now)
				cacheTidTime[pValue.TID] = now
			}
			pContext := getContext(pValue)
			go func(pValue *propertytype.Value, pContext *ValueContext) {
				handle(pValue, pContext)
				contextlistFreeChan <- pContext
			}(pValue, pContext)
		}
	}

}

func handleMapValue(tid string, pid string, value map[string]interface{}, reportTime int64, pickTime int64) {
	for k, v := range value {
		rpid := ""
		subid := ""
		if pid != "" {
			rpid = pid
			subid = k
		} else {
			rpid = k
		}
		vmap, ok := v.(map[string]interface{})
		if ok {
			handleMapValue(tid, rpid, vmap, reportTime, pickTime)
		} else {
			pItemValue := &propertytype.Value{
				TID:        tid,
				PID:        rpid,
				Mindex:     0,
				SubID:      subid,
				Value:      v,
				ReportTime: reportTime,
				PickTime:   pickTime,
			}
			valuelistChan <- pItemValue
		}

	}

}

func handle(pValue *propertytype.Value, pContext *ValueContext) {
	pContext.ValuePtr = pValue
	ptInfo, err := cache.GetThing(pValue.TID)
	if err != nil || ptInfo == nil || ptInfo.Thing.ModelID == "" {
		return
	}

	//cache.SetLastAliveTime(pValue.TID, time.Now().Unix())  //后续不在通过数据上报代替心跳了
	pContext.ThingPtr = ptInfo
	//pValue := pContext.ValuePtr
	if pValue.PickTime == 0 {
		pContext.PickTime = time.Now()
	} else {
		pContext.PickTime = time.Unix(pValue.PickTime, 0)
	}
	if ptInfo.Thing.IsProxy && !ptInfo.Thing.IsStation && ptInfo.Thing.ProxyThingID == "" {
		cache.SetLastAliveTime(pValue.TID, pContext.PickTime.Unix())
	}
	if pValue != nil && pValue.Value != nil {
		modelid := pContext.ThingPtr.Thing.ModelID
		if pValue.PID == "" && pValue.Value != nil {
			dataMap, okm := pValue.Value.(map[string]interface{})
			if okm && dataMap != nil {
				handleMapValue(pValue.TID, pValue.PID, dataMap, pValue.ReportTime, pValue.PickTime)
			}
			return
		}
		ok, pProperty := cache.GetModelProperty(modelid, pValue.PID, pValue.SubID)
		if ok && pProperty != nil {
			if pValue.SubID != "" {
				okp, pPropertyParent := cache.GetModelProperty(modelid, pValue.PID, "")
				if okp {
					pContext.ParentPropertyPtr = pPropertyParent
				}
			}

			pContext.PropertyPtr = pProperty
			if pProperty.Type == "array" || pProperty.IsArray {
				arrays, ok := pValue.Value.([]interface{})
				if ok {
					//fmt.Println("array-------------", arrays)
					for i := range arrays {

						item := arrays[i]
						//fmt.Println("array---------222222s----", item, arrays[i])
						_, ok := item.([]interface{})
						if ok {
							handleStruct(item, pContext, i)
						} else {
							pContext.ValuePtr.Value = item
							pContext.ValuePtr.Mindex = i
							handleItem(item, pContext, i)
						}

					}
				} else {
					item, oki := pValue.Value.(map[string]interface{})
					if oki {
						handleMapValue(pValue.TID, pValue.PID, item, pValue.ReportTime, pValue.PickTime)

					} else {
						item := pValue.Value
						pContext.ValuePtr.Value = item
						pContext.ValuePtr.Mindex = pValue.Mindex
						handleItem(item, pContext, pValue.Mindex)
					}
				}
			} else if pProperty.Type == "struct" {
				datas, ok := pValue.Value.([]interface{})
				if ok {
					handleStruct(datas, pContext, 0)
				} else {
					dataMap, okMap := pValue.Value.(map[string]interface{})
					if okMap && dataMap != nil {
						handleMapValue(pValue.TID, pValue.PID, dataMap, pValue.ReportTime, pValue.PickTime)
					}
				}

			} else {
				handleItem(pValue.Value, pContext, 0)
			}
		} else {
			logger.SLog.Error("********property not found ***55555555555555555555******",
				pValue.TID,
				pValue, "------pid:--------", pValue.PID,
				"------sid:--------", pValue.SubID, ok, "-------pid",
				pProperty, modelid)
		}
	} else {
		logger.SLog.Error("********pvalue not found *********", pValue.TID, pValue.PID, pValue.SubID)
	}
}

/*
func handleThrd() {
	for {
		pContext := <-contextlistChan
		pValue := pContext.ValuePtr
		handle(pValue, pContext)
		contextlistFreeChan <- pContext
	}
}
*/
